﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;
using Vit.Web;
using Vit.Web.Models;
using Vit.Web.Modules;
using Vit.Web.Providers;

namespace Vit.AdminModule.Areas.Admin.Providers
{
    public class SqlModuleProvider : IModuleProvider
    {
        private WebProvidersDb db = new WebProvidersDb();

        private ModuleInfo GetModule(string moduleName)
        {
            return db.Modules.FirstOrDefault(o => o.Name.ToLower() == moduleName.ToLower());
        }

        public Version GetInstalledModuleVersion(IModule module)
        {
            ModuleInfo mod = GetModule(module.Name);
            if (mod != null)
                return null;

            return new Version(mod.Version);
        }

        public IQueryable<ModuleInfo> GetModules()
        {
            return db.Modules.OrderBy(o => o.Name);
        }

        public bool InstallModule(IModule module, out string message)
        {
            message = null;
            ModuleInfo mod = GetModule(module.Name);
            if (mod != null)
                return false;

            bool result = true;
            string msg = string.Empty;
            ModuleEventArg e = new ModuleEventArg();
            e.LastVersion = null;
            e.Version = module.Version;

            result = module.OnInstalling(this, e, out msg);
            if (result)
            {
                try
                {
                    string sql = GetSqlScript(module.Name, module.Version.ToString(), "install");
                    if(!string.IsNullOrEmpty(sql))
                        db.Database.ExecuteSqlCommand(sql);
                }
                catch (Exception ex)
                {
                    result = false;
                    message += Environment.NewLine + ex.Message;
                }

                if (result)
                {
                    mod = new ModuleInfo()
                    {
                        Name = module.Name,
                        Version = module.Version.ToString(),
                        Fee = module.Fee,
                        Enabled = true,
                        InstalledDate = DateTime.Now,
                        Operator = PortalContext.Current.User.Name
                    };

                    db.Modules.Add(mod);
                    db.SaveChanges();
                }
            }
            message += Environment.NewLine + msg;
            result = module.OnInstalled(this, e, out msg);
            message += Environment.NewLine + msg;

            return result;
        }

        public bool UnInstallModule(IModule module, out string message)
        {
            message = null;

            bool result = true;
            string msg = string.Empty;
            ModuleEventArg e = new ModuleEventArg();
            e.LastVersion = null;
            e.Version = module.Version;

            result = module.OnUnInstalling(this, e, out message);
            if (result)
            {
                try
                {
                    string sql = GetSqlScript(module.Name, module.Version.ToString(), "uninstall");
                    if (!string.IsNullOrEmpty(sql))
                        db.Database.ExecuteSqlCommand(sql);
                }
                catch (Exception ex)
                {
                    result = false;
                    message += Environment.NewLine + ex.Message;
                }

                if (result)
                {
                    ModuleInfo mod = GetModule(module.Name);
                    if (mod != null)
                    {
                        db.Modules.Remove(mod);
                        db.SaveChanges();
                    }
                }
            }
            message += Environment.NewLine + msg;
            result = module.OnUnInstalled(this, e, out msg);
            message += Environment.NewLine + msg;

            return result;
        }

        public bool UpdateModule(IModule module, out string message)
        {
            message = null;

            ModuleInfo mod = GetModule(module.Name);
            if (mod == null || mod.Version == module.Version.ToString())
                return false;

            bool result = true;
            string msg = string.Empty;
            ModuleEventArg e = new ModuleEventArg();
            e.LastVersion = new Version(mod.Version);
            e.Version = module.Version;
            result = module.OnUpdating(this, e, out message);
            if (result)
            {
                string lastVersion = mod.Version.ToString();
                var versions = GetUpdateVersions(module.Name, lastVersion, module.Version.ToString());

                foreach (var ver in versions)
                {
                    try
                    {
                        string sql = GetSqlScript(module.Name, ver, "update");
                        if (!string.IsNullOrEmpty(sql))
                            db.Database.ExecuteSqlCommand(sql);
                        msg += Environment.NewLine + string.Format("Update successfully from version {0} to version {1}.",
                            lastVersion, ver);

                        lastVersion = ver;
                    }
                    catch (Exception ex)
                    {
                        result = false;
                        message += Environment.NewLine + ex.Message;
                        break;
                    }
                }

                if (result)
                {
                    mod.LastVersion = mod.Version;
                    mod.Version = module.Version.ToString();
                    mod.Fee = module.Fee;
                    mod.Enabled = true;
                    mod.UpdatedDate = DateTime.Now;
                    mod.Operator = PortalContext.Current.User.Name;
                    db.SaveChanges();
                }
            }
            message += Environment.NewLine + msg;
            result = module.OnUpdated(this, e, out msg);
            message += Environment.NewLine + msg;

            return result;
        }

        public bool EnableModule(IModule module, out string message)
        {
            message = null;

            ModuleInfo mod = GetModule(module.Name);
            if (mod != null)
            {
                mod.Enabled = true;
                db.SaveChanges();
                return true;
            }

            return false;
        }

        public bool DisableModule(IModule module, out string message)
        {
            message = null;

            ModuleInfo mod = GetModule(module.Name);
            if (mod != null)
            {
                mod.Enabled = false;
                db.SaveChanges();
                return true;
            }

            return false;
        }

        private IEnumerable<string> GetUpdateVersions(string name, string fromVersion, string toVersion)
        {
            List<string> versions = new List<string>();
            string path = string.Format("~/Areas/{0}/Versions/", name);
            path = HttpContext.Current.Server.MapPath(path);
            DirectoryInfo dir = new DirectoryInfo(path);
            foreach (var sd in dir.GetDirectories().OrderBy(o => o.Name))
            {
                if (string.Compare(fromVersion, sd.Name, true) < 0)
                    versions.Add(sd.Name);
                if (string.Compare(sd.Name, toVersion, true) >= 0)
                    break;
            }

            return versions;
        }

        private string GetSqlScript(string name, string version, string action)
        {
            string path = string.Format("~/Areas/{0}/Versions/{1}/{2}.sql", name, version, action);
            path = HttpContext.Current.Server.MapPath(path);
            StreamReader sr = new StreamReader(path);
            string sql = sr.ReadToEnd();
            sr.Close();
            sr.Dispose();
            return sql;
        }
    }
}